package com.zytech.zspider.eventbus.support;

import com.zytech.modules.lang.Requires;
import com.zytech.zspider.eventbus.EventBus;
import com.zytech.zspider.eventbus.EventEnvelope;
import com.zytech.zspider.eventbus.EventListener;
import com.zytech.zspider.logger.Loggers;

/**
 * @author siuming
 */
public abstract class PipelineEventListener implements EventListener {

    private final EventBus eventBus;

    /**
     * @param eventBus
     */
    protected PipelineEventListener(EventBus eventBus) {
        Requires.notNull(eventBus, "eventBus must not be null.");
        this.eventBus = eventBus;
    }

    @Override
    public final void onEvent(EventEnvelope envelope) {
        try {

            if (beforeEvent(envelope)) {
                EventTuple eventTuple = doOnEvent(envelope);
                postEventTuple(eventTuple);
            }

        } catch (Exception e) {
            handleEventException(envelope, e);
        } finally {
            afterEvent(envelope);
        }
    }


    /**
     * @param envelope
     * @return
     */
    protected boolean beforeEvent(EventEnvelope envelope) {
        return true;
    }

    /**
     * @param envelope
     * @param e
     */
    protected void handleEventException(EventEnvelope envelope, Exception e) {
        Loggers.me().error(getClass(), String.format(" handle %s occurs errors.", envelope), e);
    }

    /**
     * @param envelope
     */
    protected void afterEvent(EventEnvelope envelope) {
        Loggers.me().warn(getClass(), "{} handle end.", envelope);
    }

    /**
     * @param eventTuple
     */
    protected void postEventTuple(EventTuple eventTuple) {
        switch (eventTuple.type) {
            case QUEUE:
                getEventBus().postQueue(eventTuple.name, eventTuple.eventEnvelope);
                break;
            default:
                getEventBus().postTopic(eventTuple.name, eventTuple.eventEnvelope);
                break;
        }
    }

    /**
     * @param envelope
     * @return
     */
    protected abstract EventTuple doOnEvent(EventEnvelope envelope);

    /**
     * @return
     */
    protected final EventBus getEventBus() {
        return eventBus;
    }

    public static class EventTuple {
        public enum Type {
            QUEUE, TOPIC
        }

        private final Type type;
        private final String name;
        private final EventEnvelope eventEnvelope;

        EventTuple(Type type, String name, EventEnvelope eventEnvelope) {
            this.type = type;
            this.name = name;
            this.eventEnvelope = eventEnvelope;
        }

        public static EventTuple of(Type type, String name, EventEnvelope envelope) {
            return new EventTuple(type, name, envelope);
        }
    }
}
